
#ifndef __CORTISOL_VIDEO_H
#define __CORTISOL_VIDEO_H

#include "VideoRAW.h"

class VideoRGB : 
	public VideoRAW
{
	
	AVFrame			*alphaOutputFrame;
	unsigned char   *rgbBuffer;
	unsigned char   *alphaBuffer;
	PixelFormat		rgbOutputFormat;
	PixelFormat		alphaOutputFormat;

	SwsContext		*rgbSwsCtx;
	SwsContext		*alphaSwsCtx;

public:

	VideoRGB(const std::string &file, int times)
		:	VideoRAW(file,times),
			rgbOutputFrame(NULL),
			alphaOutputFrame(NULL),
			rgbBuffer(NULL),
			alphaBuffer(NULL),
			rgbOutputFormat((PixelFormat)0),
			alphaOutputFormat((PixelFormat)0),
			rgbSwsCtx(NULL),
			alphaSwsCtx(NULL) {

		Initialise();
	}


	virtual ~VideoRGB() {

		CleanUp();
	}

protected:

	AVFrame			*rgbOutputFrame;

	virtual void NotifyFrameDecoded(void) {

		ConvertToRGB();
	}

private:

	bool ConvertToRGB() {
		
		if(HasAlphaChannel())
			AVLib::SWScale()->sws_scale( alphaSwsCtx, alphaNativeFrame->data, alphaNativeFrame->linesize,0, alphaCodecCtx->height, alphaOutputFrame->data, alphaOutputFrame->linesize );

		AVLib::SWScale()->sws_scale( rgbSwsCtx, rgbNativeFrame->data, rgbNativeFrame->linesize,0, rgbCodecCtx->height, rgbOutputFrame->data, rgbOutputFrame->linesize );

		if(HasAlphaChannel()) {

			uint8_t *dst = rgbOutputFrame->data[0] + 3;
			uint8_t *src = alphaOutputFrame->data[0];

			uint32_t len =  rgbCodecCtx->width * rgbCodecCtx->height;

			while(len--)
			{
				*dst = *src;
				dst += 4; // RGBA32
				src += 3; // RGB24
			}
		}

		return true;
	}

	void Initialise() {

		if(!(rgbOutputFrame = AVLib::Codec()->avcodec_alloc_frame()))
			throw Exception(std::string("avcodec_alloc_frame rgb in ") + file);

		if(HasAlphaChannel() && !(alphaOutputFrame = AVLib::Codec()->avcodec_alloc_frame()))
			throw Exception(std::string("avcodec_alloc_frame alpha in ") + file);

		if(HasAlphaChannel()) {
			rgbOutputFormat   = PIX_FMT_RGBA;
			alphaOutputFormat = PIX_FMT_RGB24;
		}
		else
			rgbOutputFormat = PIX_FMT_BGR24;

		if(!(rgbBuffer = (unsigned char*)AVLib::Util()->av_malloc( AVLib::Codec()->avpicture_get_size( rgbOutputFormat, rgbCodecCtx->width, rgbCodecCtx->height ) )))
			throw Exception(std::string("av_malloc rgbBuffer in ") + file);

		if(HasAlphaChannel() && !(alphaBuffer = (unsigned char*)AVLib::Util()->av_malloc( AVLib::Codec()->avpicture_get_size( alphaOutputFormat, alphaCodecCtx->width, alphaCodecCtx->height ))))
			throw Exception(std::string("av_malloc alphaBuffer in ") + file);

		AVLib::Codec()->avpicture_fill((AVPicture*)rgbOutputFrame, rgbBuffer, rgbOutputFormat, rgbCodecCtx->width, rgbCodecCtx->height );
		if(HasAlphaChannel())
			AVLib::Codec()->avpicture_fill((AVPicture*)alphaOutputFrame, alphaBuffer, alphaOutputFormat, alphaCodecCtx->width, alphaCodecCtx->height );

		if(!(rgbSwsCtx = AVLib::SWScale()->sws_getContext( rgbCodecCtx->width, rgbCodecCtx->height, rgbCodecCtx->pix_fmt, rgbCodecCtx->width, rgbCodecCtx->height, rgbOutputFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL)))
			throw Exception("rgbSwsCtx = sws_getContext");

		if(HasAlphaChannel() && !(alphaSwsCtx = AVLib::SWScale()->sws_getContext( alphaCodecCtx->width, alphaCodecCtx->height, alphaCodecCtx->pix_fmt, alphaCodecCtx->width, alphaCodecCtx->height, alphaOutputFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL)))
			throw Exception("alphaSwsCtx = sws_getContext");

	}

	void CleanUp() {
		if(rgbOutputFrame)
			AVLib::Util()->av_free(rgbOutputFrame);
		if(alphaOutputFrame)
			AVLib::Util()->av_free(alphaOutputFrame);
		if(rgbBuffer)
			AVLib::Util()->av_free(rgbBuffer);
		if(alphaBuffer)
			AVLib::Util()->av_free(alphaBuffer);
		if(rgbSwsCtx)
			AVLib::SWScale()->sws_freeContext(rgbSwsCtx);
		if(alphaSwsCtx)
			AVLib::SWScale()->sws_freeContext(alphaSwsCtx);
	}

};

#endif /*** __CORTISOL_VIDEO_H ***/

